import { DependencyContainer } from "tsyringe";
import multer, { Options } from 'multer'
import { HttpError, ROOT, Request, Response, generateRandomString } from "@sker/core";
import { extname, join } from "path";
import { Readable } from "stream";
import multerS3 from 'multer-s3';
import { createS3 } from '@sker/s3'
import type { Request as ExpressRequest } from 'express'
import { GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

export interface File {
    fieldname: string;
    originalname: string;
    encoding: string;
    mimetype: string;
    size: number;
    stream: Readable;
    destination: string;
    filename: string;
    path: string;
    buffer: Buffer;
    key?: string;
    location?: string;
}
export interface MulterHandler {
    (m: File): Promise<any>
}
export const useMulterS3 = async (injector: DependencyContainer, handler: MulterHandler, name: string = 'file', options: Options = {}) => {
    const req = injector.resolve(Request)
    const res = injector.resolve(Response)
    const query = (req as ExpressRequest).query;
    const is_public = !!(query.is_public === 'true');
    return createS3(injector, async (s3, config) => {
        const upload = multer({
            storage: multerS3({
                s3,
                bucket: (req, file, callback) => {
                    const bucket = is_public ? config.publicBucket : config.privateBucket;
                    callback(null, bucket)
                },
                acl: 'public-read',
                contentType: multerS3.AUTO_CONTENT_TYPE,
                metadata: (req, file, cb) => {
                    const filename = file.originalname;
                    const ext = extname(filename);
                    cb(null, { name: `${generateRandomString(12)}${ext}`, fieldName: file.filename, type: file.mimetype });
                },
                key: (req, file, cb) => {
                    cb(null, Date.now().toString()+'-'+file.originalname)
                }
            }),
            ...options
        })
        return new Promise((resolve, reject) => {
            upload.single(name)(req, res, async (err) => {
                if (err) return reject(err)
                try {
                    const file = req.file as File;
                    if (file) {
                        const key = (req.file as any).key;
                        if (!is_public) {
                            const command = new GetObjectCommand({
                                Bucket: config.privateBucket,
                                Key: key,
                            });
                            const url = await getSignedUrl(s3, command, {
                                expiresIn: 60 * 60,
                            });
                            file.location = url;
                        }
                        const result = await handler(file as any)
                        resolve(result)
                    } else {
                        reject(new HttpError(`上传失败`))
                    }
                } catch (e) {
                    return reject(err)
                }
            })
        })
    })
}

export default useMulterS3;
